<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Element Plus 页面设计器 - 组件包装器系统</title>
  <link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css">
  <script src="https://unpkg.com/vue@3"></script>
  <script src="https://unpkg.com/element-plus"></script>
  <script src="https://unpkg.com/@element-plus/icons-vue"></script>
  <script src="https://unpkg.com/interactjs/dist/interact.min.js"></script>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
    }
    
    body {
      background: linear-gradient(135deg, #2c3e50, #4ca1af);
      min-height: 100vh;
      padding: 20px;
      color: #333;
    }
    
    .designer-system {
      display: grid;
      grid-template-columns: 220px 1fr 320px;
      gap: 20px;
      width: 100%;
      max-width: 1600px;
      min-height: 85vh;
      margin: 0 auto;
      background: rgba(255, 255, 255, 0.95);
      border-radius: 16px;
      box-shadow: 0 12px 35px rgba(0, 0, 0, 0.3);
      overflow: hidden;
      position: relative;
      z-index: 10;
    }
    
    /* 系统头部 */
    .system-header {
      background: linear-gradient(90deg, #1a56db, #3498db);
      padding: 20px 30px;
      color: white;
      grid-column: 1 / -1;
      border-radius: 16px 16px 0 0;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }
    
    .header-left {
      display: flex;
      align-items: center;
      gap: 15px;
    }
    
    .main-title {
      font-size: 1.5rem;
      font-weight: 700;
    }
    
    .header-tabs {
      display: flex;
      gap: 5px;
      background: rgba(255, 255, 255, 0.15);
      padding: 5px;
      border-radius: 30px;
    }
    
    .tab-item {
      padding: 6px 15px;
      border-radius: 20px;
      cursor: pointer;
      transition: all 0.3s;
    }
    
    .tab-item.active {
      background: white;
      color: #1a56db;
    }
    
    /* 组件库面板 */
    .component-panel {
      padding: 20px;
      background: #f8f9ff;
      border-right: 1px solid #e0e7ff;
    }
    
    .panel-title {
      font-size: 1.1rem;
      margin-bottom: 15px;
      color: #1a56db;
      display: flex;
      align-items: center;
      gap: 8px;
    }
    
    .component-list {
      display: flex;
      flex-direction: column;
      gap: 12px;
    }
    
    .component-item {
      background: white;
      border-radius: 8px;
      padding: 12px;
      box-shadow: 0 3px 8px rgba(0, 0, 0, 0.05);
      transition: all 0.3s ease;
      border: 1px solid #e4e7ed;
      cursor: grab;
    }
    
    .component-item:hover {
      transform: translateY(-3px);
      box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
      border-color: #1a56db;
    }
    
    .component-item:active {
      cursor: grabbing;
    }
    
    .component-icon {
      margin-bottom: 8px;
      display: inline-flex;
      background: #dbeafe;
      width: 32px;
      height: 32px;
      border-radius: 6px;
      align-items: center;
      justify-content: center;
    }
    
    .component-name {
      font-weight: 600;
      font-size: 0.95rem;
    }
    
    /* 设计区域 */
    .design-area {
      padding: 20px;
      display: flex;
      flex-direction: column;
      background: #f1f5f9;
      position: relative;
      min-height: 500px;
    }
    
    .canvas-container {
      position: relative;
      background: white;
      border-radius: 8px;
      flex-grow: 1;
      overflow: auto;
      box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.05);
      padding: 25px;
      border: 1px solid #cbd5e1;
    }
    
    .design-canvas {
      min-height: 600px;
      min-width: 100%;
      background-color: #fff;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
      position: relative;
    }
    
    /* 包装器 */
    .component-wrapper {
      position: absolute;
      cursor: move;
      padding: 10px;
      transition: all 0.15s;
      box-sizing: content-box;
      z-index: 1;
    }
    
    .component-wrapper.active {
      background: rgba(59, 130, 246, 0.1);
      box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
      z-index: 10;
    }
    
    .wrapper-toolbar {
      position: absolute;
      bottom: 100%;
      left: 0;
      display: none;
      background: white;
      box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
      border-radius: 6px;
      padding: 5px;
      z-index: 20;
    }
    
    .component-wrapper.active .wrapper-toolbar {
      display: flex;
    }
    
    .toolbar-btn {
      width: 28px;
      height: 28px;
      border-radius: 4px;
      background: white;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      transition: all 0.2s;
    }
    
    .toolbar-btn:hover {
      background: #eff6ff;
      color: #1d4ed8;
    }
    
    .drag-handle {
      cursor: move;
    }
    
    .wrapper-content {
      width: 100%;
      height: 100%;
      overflow: hidden;
    }
    
    .resize-handle {
      position: absolute;
      background: #3b82f6;
      border-radius: 50%;
      width: 10px;
      height: 10px;
      z-index: 11;
      display: none;
    }
    
    .component-wrapper.active .resize-handle {
      display: block;
    }
    
    .resize-top-left {
      top: -5px;
      left: -5px;
      cursor: nwse-resize;
    }
    
    .resize-top-right {
      top: -5px;
      right: -5px;
      cursor: nesw-resize;
    }
    
    .resize-bottom-left {
      bottom: -5px;
      left: -5px;
      cursor: nesw-resize;
    }
    
    .resize-bottom-right {
      bottom: -5px;
      right: -5px;
      cursor: nwse-resize;
    }
    
    /* 属性面板 */
    .property-panel {
      padding: 20px;
      background: #f8f9ff;
      border-left: 1px solid #e0e7ff;
      overflow-y: auto;
    }
    
    .property-group {
      background: white;
      border-radius: 12px;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
      padding: 18px;
      margin-bottom: 20px;
    }
    
    .property-header {
      display: flex;
      align-items: center;
      gap: 10px;
      padding-bottom: 12px;
      margin-bottom: 12px;
      border-bottom: 1px solid #e5e7eb;
    }
    
    .property-title {
      font-weight: 600;
      color: #1e293b;
      font-size: 1.05rem;
    }
    
    .property-row {
      display: grid;
      grid-template-columns: 100px 1fr;
      align-items: center;
      margin-bottom: 15px;
    }
    
    .property-label {
      font-weight: 500;
      color: #4b5563;
      font-size: 0.9rem;
    }
    
    .status-indicator {
      display: flex;
      align-items: center;
      gap: 8px;
      margin-top: 15px;
      padding: 12px;
      background: #eff6ff;
      border-radius: 8px;
    }
    
    .indicator-dot {
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background: #10b981;
    }
    
    .indicator-text {
      font-size: 0.85rem;
      color: #4b5563;
    }
    
    /* 操作栏 */
    .action-bar {
      padding: 15px 20px;
      border-top: 1px solid #e0e7ff;
      grid-column: 1 / -1;
      display: flex;
      justify-content: flex-end;
      gap: 15px;
    }
    
    /* 响应式调整 */
    @media (max-width: 1200px) {
      .designer-system {
        grid-template-columns: 200px 1fr;
      }
      .property-panel {
        display: none;
      }
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="designer-system">
      <div class="system-header">
        <div class="header-left">
          <el-icon size="32"><element-plus /></el-icon>
          <div class="main-title">Element Plus 页面设计器</div>
        </div>
        <div class="header-tabs">
          <div class="tab-item active">设计模式</div>
          <div class="tab-item">预览模式</div>
          <div class="tab-item">代码导出</div>
        </div>
      </div>
      
      <div class="component-panel">
        <div class="panel-title">
          <el-icon><component /></el-icon> 组件库
        </div>
        <div class="component-list">
          <div class="component-item" draggable="true" data-type="button">
            <div class="component-icon">
              <el-icon></el-icon>
            </div>
            <div class="component-name">按钮</div>
          </div>
          <div class="component-item" draggable="true" data-type="input">
            <div class="component-icon">
              <el-icon><edit /></el-icon>
            </div>
            <div class="component-name">输入框</div>
          </div>
          <div class="component-item" draggable="true" data-type="card">
            <div class="component-icon">
              <el-icon><credit-card /></el-icon>
            </div>
            <div class="component-name">卡片</div>
          </div>
          <div class="component-item" draggable="true" data-type="progress">
            <div class="component-icon">
              <el-icon><loading /></el-icon>
            </div>
            <div class="component-name">进度条</div>
          </div>
          <div class="component-item" draggable="true" data-type="switch">
            <div class="component-icon">
              <el-icon><switch /></el-icon>
            </div>
            <div class="component-name">开关</div>
          </div>
          <div class="component-item" draggable="true" data-type="avatar">
            <div class="component-icon">
              <el-icon><user /></el-icon>
            </div>
            <div class="component-name">头像</div>
          </div>
        </div>
      </div>
      
      <div class="design-area">
        <div class="panel-title">
          <el-icon><monitor /></el-icon> 设计区域
        </div>
        <div class="canvas-container">
          <div class="design-canvas" id="designCanvas">
            <!-- 组件包装器将会动态添加到这里 -->
          </div>
        </div>
      </div>
      
      <div class="property-panel">
        <div class="panel-title">
          <el-icon><set-up /></el-icon> 属性配置
        </div>
        <div class="property-group">
          <div class="property-header">
            <el-icon><set-up /></el-icon>
            <div class="property-title">位置和大小</div>
          </div>
          <div class="property-row">
            <div class="property-label">X坐标:</div>
            <el-input v-model="activeWrapper.x" size="small"></el-input>
          </div>
          <div class="property-row">
            <div class="property-label">Y坐标:</div>
            <el-input v-model="activeWrapper.y" size="small"></el-input>
          </div>
          <div class="property-row">
            <div class="property-label">宽度:</div>
            <el-input v-model="activeWrapper.width" size="small"></el-input>
          </div>
          <div class="property-row">
            <div class="property-label">高度:</div>
            <el-input v-model="activeWrapper.height" size="small"></el-input>
          </div>
        </div>
        
        <div class="property-group">
          <div class="property-header">
            <el-icon><document /></el-icon>
            <div class="property-title">组件属性</div>
          </div>
          <template v-if="activeWrapper.type">
            <div v-if="activeWrapper.type === 'button'" class="property-row">
              <div class="property-label">按钮文本:</div>
              <el-input v-model="activeComponentProps.buttonText" size="small"></el-input>
            </div>
            <div v-if="activeWrapper.type === 'input'" class="property-row">
              <div class="property-label">占位符:</div>
              <el-input v-model="activeComponentProps.placeholder" size="small"></el-input>
            </div>
            <div v-if="activeWrapper.type === 'card'" class="property-row">
              <div class="property-label">标题:</div>
              <el-input v-model="activeComponentProps.cardTitle" size="small"></el-input>
            </div>
            <div v-if="activeWrapper.type === 'progress'" class="property-row">
              <div class="property-label">进度:</div>
              <el-slider v-model="activeComponentProps.progressValue"></el-slider>
            </div>
            <div v-if="activeWrapper.type === 'button'" class="property-row">
              <div class="property-label">按钮类型:</div>
              <el-select v-model="activeComponentProps.buttonType" size="small">
                <el-option label="主要" value="primary"></el-option>
                <el-option label="成功" value="success"></el-option>
                <el-option label="警告" value="warning"></el-option>
                <el-option label="危险" value="danger"></el-option>
              </el-select>
            </div>
            <div v-if="!activeWrapper.type" class="property-row">
              <div class="property-label">请选择组件</div>
            </div>
          </template>
        </div>
        
        <div class="status-indicator">
          <div class="indicator-dot"></div>
          <div class="indicator-text">已选择: {{ activeWrapper.type ? activeWrapper.type : '无' }}</div>
        </div>
      </div>
      
      <div class="action-bar">
        <el-button type="primary">保存设计</el-button>
        <el-button type="success">导出代码</el-button>
        <el-button>重置</el-button>
      </div>
    </div>
  </div>

  <script>
    const { createApp, ref, reactive, onMounted } = Vue
    
    const app = createApp({
      setup() {
        const canvasRef = ref(null)
        const wrappers = reactive([])
        const activeWrapperId = ref(null)
        
        // 当前活动包装器的信息
        const activeWrapper = reactive({
          id: null,
          type: null,
          x: 0,
          y: 0,
          width: 0,
          height: 0
        })
        
        // 活动组件属性
        const activeComponentProps = reactive({
          buttonText: '按钮',
          placeholder: '请输入内容',
          cardTitle: '卡片标题',
          progressValue: 50,
          buttonType: 'primary'
        })
        
        // 初始化Interact.js
        function initInteract() {
          // 设置可放置区域
          interact('#designCanvas').dropzone({
            accept: '.component-item',
            ondrop: function(event) {
              const type = event.relatedTarget.dataset.type
              const rect = canvasRef.value.getBoundingClientRect()
              const x = event.clientX - rect.left
              const y = event.clientY - rect.top
              
              addWrapper(type, x, y)
            }
          })
          
          // 包装器选择处理
          interact('.component-wrapper')
            .on('tap', function(event) {
              const wrapperId = event.currentTarget.dataset.id
              selectWrapper(wrapperId)
            })
        }
        
        // 添加包装器
        function addWrapper(type, x, y) {
          const id = `wrapper-${Date.now()}`
          
          // 创建默认大小的包装器
          const wrapper = {
            id: id,
            type: type,
            x: x,
            y: y,
            width: type === 'button' ? 120 : 
                  type === 'input' ? 240 : 
                  type === 'card' ? 280 : 
                  type === 'progress' ? 220 : 120,
            height: type === 'button' ? 40 : 
                   type === 'input' ? 40 : 
                   type === 'card' ? 160 : 
                   type === 'progress' ? 30 : 40
          }
          
          wrappers.push(wrapper)
          
          // Use nextTick to wait for Vue to render the wrapper
          Vue.nextTick(() => {
            const wrapperElement = document.querySelector(`[data-id="${id}"]`)
            if (wrapperElement) {
              initWrapperInteractions(id)
              selectWrapper(id)
            } else {
              console.error('Failed to find wrapper element for initialization')
            }
          })
        }
        
        // 初始化包装器的交互
        function initWrapperInteractions(id) {
          const wrapper = document.querySelector(`[data-id="${id}"]`)
          
          // 设置拖拽
          interact(wrapper).draggable({
            modifiers: [
              interact.modifiers.restrictRect({
                restriction: 'parent'
              })
            ],
            onmove: function(event) {
              const target = event.target
              const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx
              const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy
              
              target.style.transform = `translate(${x}px, ${y}px)`
              target.setAttribute('data-x', x)
              target.setAttribute('data-y', y)
              
              // 更新坐标数据
              updateWrapperPosition(id, x, y)
            }
          })
          
          // 设置大小调整
          const resizeOptions = {
            edges: {
              left: '.resize-top-left, .resize-bottom-left',
              right: '.resize-top-right, .resize-bottom-right',
              bottom: '.resize-bottom-left, .resize-bottom-right',
              top: '.resize-top-left, .resize-top-right'
            },
            modifiers: [
              interact.modifiers.restrictEdges({
                outer: 'parent'
              })
            ],
            onmove: function(event) {
              const target = event.target
              const rect = target.getBoundingClientRect()
              const wrapperId = target.closest('.component-wrapper').dataset.id
              
              let width = rect.width
              let height = rect.height
              
              // 根据操作方向更新大小
              if (event.edges.left) {
                width -= event.delta.x
                updateWrapperPosition(wrapperId, activeWrapper.x + event.delta.x, activeWrapper.y)
              }
              
              if (event.edges.right) {
                width += event.delta.x
              }
              
              if (event.edges.top) {
                height -= event.delta.y
                updateWrapperPosition(wrapperId, activeWrapper.x, activeWrapper.y + event.delta.y)
              }
              
              if (event.edges.bottom) {
                height += event.delta.y
              }
              
              // 设置最小尺寸
              width = Math.max(width, 80)
              height = Math.max(height, 30)
              
              // 更新包装器大小
              updateWrapperSize(wrapperId, width, height)
            }
          }
          
          interact(wrapper).resizable(resizeOptions)
          
          // 设置操作按钮的事件处理
          const deleteBtn = wrapper.querySelector('.delete-btn')
          if (deleteBtn) {
            deleteBtn.addEventListener('click', (e) => {
              e.stopPropagation()
              removeWrapper(id)
            })
          }
        }
        
        // 更新包装器位置
        function updateWrapperPosition(id, x, y) {
          const index = wrappers.findIndex(w => w.id === id)
          if (index !== -1) {
            wrappers[index].x = x
            wrappers[index].y = y
          }
          
          if (activeWrapperId.value === id) {
            activeWrapper.x = x
            activeWrapper.y = y
          }
        }
        
        // 更新包装器大小
        function updateWrapperSize(id, width, height) {
          const index = wrappers.findIndex(w => w.id === id)
          if (index !== -1) {
            wrappers[index].width = width
            wrappers[index].height = height
          }
          
          if (activeWrapperId.value === id) {
            activeWrapper.width = width
            activeWrapper.height = height
          }
        }
        
        // 选择包装器
        function selectWrapper(id) {
          activeWrapperId.value = id
          
          const wrapper = wrappers.find(w => w.id === id)
          if (wrapper) {
            activeWrapper.id = wrapper.id
            activeWrapper.type = wrapper.type
            activeWrapper.x = wrapper.x
            activeWrapper.y = wrapper.y
            activeWrapper.width = wrapper.width
            activeWrapper.height = wrapper.height
          }
          
          // 更新所有包装器的选中状态
          document.querySelectorAll('.component-wrapper').forEach(el => {
            el.classList.remove('active')
          })
          
          const wrapperEl = document.querySelector(`[data-id="${id}"]`)
          if (wrapperEl) {
            wrapperEl.classList.add('active')
          }
        }
        
        // 移除包装器
        function removeWrapper(id) {
          const index = wrappers.findIndex(w => w.id === id)
          if (index !== -1) {
            wrappers.splice(index, 1)
            
            if (activeWrapperId.value === id) {
              activeWrapperId.value = null
              activeWrapper.id = null
              activeWrapper.type = null
            }
          }
        }
        
        // 渲染组件
        function renderComponent(type, props) {
          let template = '<div style="width:100%;height:100%;">'
          
          switch(type) {
            case 'button':
              template += `<el-button type="${props.buttonType || 'primary'}">${props.buttonText || '按钮'}</el-button>`
              break
            case 'input':
              template += `<el-input placeholder="${props.placeholder || '请输入内容'}"></el-input>`
              break
            case 'card':
              template += `
                <el-card>
                  <template #header>
                    <div class="card-header">
                      <span>${props.cardTitle || '卡片标题'}</span>
                    </div>
                  </template>
                  <div>这是一个可配置的卡片组件</div>
                </el-card>
              `
              break
            case 'progress':
              template += `<el-progress :percentage="${props.progressValue || 50}"></el-progress>`
              break
            case 'switch':
              template += `<el-switch v-model="value"></el-switch>`
              break
            case 'avatar':
              template += `<el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></el-avatar>`
              break
            default:
              template += `<div>未配置组件</div>`
          }
          
          template += '</div>'
          return template
        }
        
        // 初始化
        onMounted(() => {
          canvasRef.value = document.getElementById('designCanvas')
          initInteract()
          
          // 添加示例包装器
          addWrapper('card', 50, 50)
          addWrapper('button', 300, 100)
          addWrapper('input', 50, 300)
        })
        
        return {
          wrappers,
          activeWrapper,
          activeComponentProps,
          selectWrapper,
          renderComponent
        }
      }
    })
    
    // 使用 Element Plus
    app.use(ElementPlus)
    
    // 注册图标
    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
      app.component(key, component)
    }
    
    app.mount('#app')
  </script>
  
  <!-- 用于动态插入包装器的模板 -->
  <script type="text/x-template" id="component-wrapper-template">
    <div
      class="component-wrapper"
      :data-id="wrapper.id"
      :style="{
        left: wrapper.x + 'px',
        top: wrapper.y + 'px',
        width: wrapper.width + 'px',
        height: wrapper.height + 'px'
      }"
      @click="selectWrapper(wrapper.id)"
    >
      <div class="wrapper-toolbar">
        <div class="toolbar-btn drag-handle">
          <el-icon><rank /></el-icon>
        </div>
        <div class="toolbar-btn delete-btn" @click.stop="removeWrapper(wrapper.id)">
          <el-icon><delete /></el-icon>
        </div>
      </div>
      
      <div class="wrapper-content" v-html="renderComponent(wrapper.type, $root.activeComponentProps)"></div>
      
      <div class="resize-handle resize-top-left"></div>
      <div class="resize-handle resize-top-right"></div>
      <div class="resize-handle resize-bottom-left"></div>
      <div class="resize-handle resize-bottom-right"></div>
    </div>
  </script>
</body>
</html>